#ifndef __TUI_H__
#define __TUI_H__

#include <stdio.h>
#include <signal.h>

#ifdef _WIN32
    #include <windows.h>
    #include <process.h>
#else
    #include <unistd.h>
    #include <sys/ioctl.h>
#endif

static int tui_run;
static void (*tui_close_handler)(int);
static void (*tui_resize_handler)(int, int, int);

void get_terminal_size(int *rows, int *cols) {
    #ifdef _WIN32
        static CONSOLE_SCREEN_BUFFER_INFO csbi;
        GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
        *cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
        *rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    #else
        static struct winsize ws;
        ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
        *cols = ws.ws_col;
        *rows = ws.ws_row;
    #endif
}

void set_terminal_size(int rows, int cols) {
    #ifdef _WIN32
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        COORD coord = {0, 0};
        SMALL_RECT rect = {0, 0, cols - 1, rows - 1};
        SetConsoleWindowInfo(hConsole, TRUE, &rect);
        SetConsoleScreenBufferSize(hConsole, coord);
    #else
        static struct winsize ws;
        ws.ws_row = rows;
        ws.ws_col = cols;
        ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws);
    #endif
}

void clear_terminal() {
    #ifdef _WIN32
        system("cls");
    #else
        system("clear");
    #endif
}

void init_terminal(void) {
    #ifdef _WIN32
        SetConsoleOutputCP(CP_UTF8);
        SetConsoleCP(CP_UTF8);
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
        SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
    #else
        setlocale(LC_ALL, "");
        printf("\033[?25l");
    #endif
    clear_terminal();
    tui_run = 1;
}
void close_terminal(void) {
    #ifdef _WIN32
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
        SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
    #else
        printf("\033[?25h");
    #endif
}
void set_terminal_title(const char *title) {
    #ifdef _WIN32
        SetConsoleTitle(title);
    #else
        printf("\033]0;%s\007", title);
    #endif
}

void set_terminal_resize_handler(void (*handler)(int, int, int)) {
    tui_resize_handler = handler;
}
void set_terminal_close_handler(void (*handler)(int)) {
    signal(SIGINT, handler);
}

#ifdef _WIN32
static void tui_resize_handler_holder(int signum, int rows, int cols){
    if(tui_resize_handler)
        tui_resize_handler(signum, rows, cols);
}
static unsigned __stdcall poll_window_size(void *arg){
    static int last_rows, last_cols;
    static int rows, cols;
    for( ; ; ){
        get_terminal_size(&rows, &cols);
        if(rows != last_rows || cols != last_cols){
            last_rows = rows;
            last_cols = cols;
            tui_resize_handler_holder(0, rows, cols);
        }
        Sleep(100);
    }
    return 0;
}
#else
static void tui_resize_handler_holder(int signum){
    static int rows, cols;
    if(tui_resize_handler){
        get_terminal_size(&rows, &cols);
        tui_resize_handler(signum, rows, cols);
    }
}
#endif
static void tui_close_handler_holder(int signum){
    tui_run = 0;
    if(tui_close_handler)
        tui_close_handler(signum);
    close_terminal();
}

int tui_loop(void){
    signal(SIGINT, tui_close_handler_holder);
    #ifdef _WIN32
        HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, poll_window_size, 0, 0, NULL);
        if(hThread == NULL)
            return -1;
        for( ;tui_run ; ){
        }
        CloseHandle(hThread);
    #else
        signal(SIGWINCH, tui_resize_handler_holder);
        for( ;tui_run ; ){
        }
    #endif
    return 0;
}

#endif // __TUI_H__
